TypeScriptã®'infer'ããŒã¯ãŒãã«é¢ããå æ¬çãªã¬ã€ããæ¡ä»¶ä»ãåãšçµã¿åãããŠã匷åãªåæœåºãšæäœãå®çŸããæ¹æ³ã解説ããŸãã
TypeScript Inferããã¹ã¿ãŒããïŒé«åºŠãªåæäœã®ããã®æ¡ä»¶ä»ãåæœåº
TypeScriptã®åã·ã¹ãã ã¯éåžžã«åŒ·åã§ãéçºè
ãå
ç¢ã§ä¿å®æ§ã®é«ãã¢ããªã±ãŒã·ã§ã³ãäœæããããšãå¯èœã«ããŸãããã®åãå¯èœã«ããéèŠãªæ©èœã®1ã€ããæ¡ä»¶ä»ãåãšçµã¿åãããŠäœ¿çšãããinfer
ããŒã¯ãŒãã§ãããã®çµã¿åããã¯ãè€éãªåæ§é ããç¹å®ã®åãæœåºããããã®ã¡ã«ããºã ãæäŸããŸãããã®ããã°èšäºã§ã¯ãinfer
ããŒã¯ãŒããæ·±ãæãäžãããã®æ©èœãšé«åºŠãªäœ¿çšäŸã玹ä»ããŸããAPIã€ã³ã¿ã©ã¯ã·ã§ã³ããè€éãªããŒã¿æ§é ã®æäœãŸã§ãããŸããŸãªãœãããŠã§ã¢éçºã·ããªãªã«é©çšã§ããå®è·µçãªäŸãæ€èšããŸãã
æ¡ä»¶ä»ãåãšã¯ïŒ
infer
ã«ã€ããŠè©³ãã説æããåã«ãæ¡ä»¶ä»ãåã«ã€ããŠç°¡åã«åŸ©ç¿ããŸããããTypeScriptã®æ¡ä»¶ä»ãåã䜿çšãããšãJavaScriptã®äžé
æŒç®åãšåæ§ã«ãæ¡ä»¶ã«åºã¥ããŠåãå®çŸ©ã§ããŸããåºæ¬çãªæ§æã¯æ¬¡ã®ãšããã§ãã
T extends U ? X : Y
ããã¯æ¬¡ã®ããã«èªã¿ãŸãïŒãåT
ãåU
ã«å²ãåœãŠå¯èœã§ããã°ãåã¯X
ã§ãããããã§ãªããã°åã¯Y
ã§ãããã
äŸïŒ
type IsString<T> = T extends string ? true : false;
type StringResult = IsString<string>; // type StringResult = true
type NumberResult = IsString<number>; // type NumberResult = false
infer
ããŒã¯ãŒãã®ç޹ä»
infer
ããŒã¯ãŒãã¯ãæ¡ä»¶ä»ãåã®extends
å¥å
ã§äœ¿çšããããã§ãã¯ãããŠããåããæšæž¬ã§ããå倿°ã宣èšããŸããæ¬è³ªçã«ã¯ãåŸã§äœ¿çšããããã«åã®éšåãããã£ããã£ãã§ããŸãã
åºæ¬çãªæ§æïŒ
type MyType<T> = T extends (infer U) ? U : never;
ãã®äŸã§ã¯ãT
ãäœããã®åã«å²ãåœãŠå¯èœã§ããå ŽåãTypeScriptã¯U
ã®åãæšæž¬ããããšããŸããæšæž¬ãæåããå Žåãåã¯U
ã«ãªããŸãããã以å€ã®å Žåã¯never
ã«ãªããŸãã
infer
ã®ç°¡åãªäŸ
1. 颿°ã®æ»ãå€ã®åãæšæž¬ãã
äžè¬çãªäœ¿çšäŸã¯ã颿°ã®æ»ãå€ã®åãæšæž¬ããããšã§ãã
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
function add(a: number, b: number): number {
return a + b;
}
type AddReturnType = ReturnType<typeof add>; // type AddReturnType = number
function greet(name: string): string {
return `Hello, ${name}!`;
}
type GreetReturnType = ReturnType<typeof greet>; // type GreetReturnType = string
ãã®äŸã§ã¯ãReturnType<T>
ã¯é¢æ°åT
ãå
¥åãšããŠåãåããŸããT
ããä»»æã®åŒæ°ãåãåãå€ãè¿ã颿°ã«å²ãåœãŠå¯èœãã©ããããã§ãã¯ããŸããå²ãåœãŠå¯èœã§ããã°ãæ»ãå€ã®åãR
ãšããŠæšæž¬ããŠè¿ããŸãããã以å€ã®å Žåã¯any
ãè¿ããŸãã
2. é åèŠçŽ ã®åãæšæž¬ãã
ãã1ã€ã®äŸ¿å©ãªã·ããªãªã¯ãé åããèŠçŽ ã®åãæœåºããããšã§ãã
type ArrayElementType<T> = T extends (infer U)[] ? U : never;
type NumberArrayType = ArrayElementType<number[]>; // type NumberArrayType = number
type StringArrayType = ArrayElementType<string[]>; // type StringArrayType = string
type MixedArrayType = ArrayElementType<(string | number)[]>; // type MixedArrayType = string | number
type NotAnArrayType = ArrayElementType<number>; // type NotAnArrayType = never
ããã§ãArrayElementType<T>
ã¯T
ãé
ååã§ãããã©ããããã§ãã¯ããŸããããã§ããã°ãèŠçŽ ã®åãU
ãšããŠæšæž¬ããŠè¿ããŸããããã§ãªãå Žåã¯ãnever
ãè¿ããŸãã
infer
ã®é«åºŠãªäœ¿çšäŸ
1. ã³ã³ã¹ãã©ã¯ã¿ã®ãã©ã¡ãŒã¿ãæšæž¬ãã
infer
ã䜿çšããŠãã³ã³ã¹ãã©ã¯ã¿é¢æ°ã®ãã©ã¡ãŒã¿åãæœåºã§ããŸãã
type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never;
class Person {
constructor(public name: string, public age: number) {}
}
type PersonConstructorParams = ConstructorParameters<typeof Person>; // type PersonConstructorParams = [string, number]
class Point {
constructor(public x: number, public y: number) {}
}
type PointConstructorParams = ConstructorParameters<typeof Point>; // type PointConstructorParams = [number, number]
ãã®å ŽåãConstructorParameters<T>
ã¯ã³ã³ã¹ãã©ã¯ã¿é¢æ°åT
ãåãåããŸããã³ã³ã¹ãã©ã¯ã¿ãã©ã¡ãŒã¿ã®åãP
ãšããŠæšæž¬ããã¿ãã«ãšããŠè¿ããŸãã
2. ãªããžã§ã¯ãåããããããã£ãæœåºãã
infer
ã¯ããããã³ã°ãããåãšæ¡ä»¶ä»ãåã䜿çšããŠããªããžã§ã¯ãåããç¹å®ã®ããããã£ãæœåºããããã«ã䜿çšã§ããŸãã
type PickByType<T, K extends keyof T, U> = {
[P in K as T[P] extends U ? P : never]: T[P];
};
interface User {
id: number;
name: string;
age: number;
email: string;
isActive: boolean;
}
type StringProperties = PickByType<User, keyof User, string>; // type StringProperties = { name: string; email: string; }
type NumberProperties = PickByType<User, keyof User, number>; // type NumberProperties = { id: number; age: number; }
//An interface representing geographic coordinates.
interface GeoCoordinates {
latitude: number;
longitude: number;
altitude: number;
country: string;
city: string;
timezone: string;
}
type NumberCoordinateProperties = PickByType<GeoCoordinates, keyof GeoCoordinates, number>; // type NumberCoordinateProperties = { latitude: number; longitude: number; altitude: number; }
ããã§ãPickByType<T, K, U>
ã¯ãå€ãåU
ã«å²ãåœãŠå¯èœãªT
ã®ããããã£ïŒK
å
ã®ããŒãæã€ïŒã®ã¿ãå«ãæ°ããåãäœæããŸãããããã³ã°ãããåã¯T
ã®ããŒãå埩åŠçããæ¡ä»¶ä»ãåã¯æå®ãããåãšäžèŽããªãããŒããã£ã«ã¿ãªã³ã°ããŸãã
3. Promiseã®æäœ
Promise
ã®è§£æ±ºãããåãæšæž¬ã§ããŸãã
type Awaited<T> = T extends Promise<infer U> ? U : T;
async function fetchData(): Promise<string> {
return 'Data from API';
}
type FetchDataType = Awaited<ReturnType<typeof fetchData>>; // type FetchDataType = string
async function fetchNumbers(): Promise<number[]> {
return [1, 2, 3];
}
type FetchedNumbersType = Awaited<ReturnType<typeof fetchNumbers>>; //type FetchedNumbersType = number[]
Awaited<T>
åã¯ãPromiseã§ãããšäºæ³ãããåT
ãåãåããŸããæ¬¡ã«ãåã¯Promiseã®è§£æ±ºãããåU
ãæšæž¬ãããããè¿ããŸããT
ãpromiseã§ãªãå Žåã¯ãTãè¿ããŸããããã¯ãTypeScriptã®æ°ããããŒãžã§ã³ã®çµã¿èŸŒã¿ãŠãŒãã£ãªãã£åã§ãã
4. Promiseã®é åã®åãæœåºãã
Awaited
ãšé
ååæšè«ãçµã¿åããããšãPromisesã®é
åã«ãã£ãŠè§£æ±ºãããåãæšæž¬ã§ããŸããããã¯ãPromise.all
ãæ±ãå Žåã«ç¹ã«åœ¹ç«ã¡ãŸãã
type PromiseArrayReturnType<T extends Promise<any>[]> = {
[K in keyof T]: Awaited<T[K]>;
};
async function getUSDRate(): Promise<number> {
return 0.0069;
}
async function getEURRate(): Promise<number> {
return 0.0064;
}
const rates = [getUSDRate(), getEURRate()];
type RatesType = PromiseArrayReturnType<typeof rates>;
// type RatesType = [number, number]
ãã®äŸã§ã¯ãæåã«ã2ã€ã®éåæé¢æ°getUSDRate
ãšgetEURRate
ãå®çŸ©ããçºæ¿ã¬ãŒãã®ååŸãã·ãã¥ã¬ãŒãããŸããæ¬¡ã«ãPromiseArrayReturnType
ãŠãŒãã£ãªãã£åã¯ãé
åå
ã®åPromise
ãã解決ãããåãæœåºããåèŠçŽ ã察å¿ããPromiseã®åŸ
æ©åã§ããã¿ãã«åãçæããŸãã
ããŸããŸãªãã¡ã€ã³ã«ãããå®è·µçãªäŸ
1. Eã³ããŒã¹ã¢ããªã±ãŒã·ã§ã³
APIãã補åã®è©³çްãååŸããEã³ããŒã¹ã¢ããªã±ãŒã·ã§ã³ãèããŠã¿ãŸããããinfer
ã䜿çšããŠã補åããŒã¿ã®åãæœåºã§ããŸãã
interface Product {
id: number;
name: string;
price: number;
description: string;
imageUrl: string;
category: string;
rating: number;
countryOfOrigin: string;
}
async function fetchProduct(productId: number): Promise<Product> {
// Simulate API call
return new Promise((resolve) => {
setTimeout(() => {
resolve({
id: productId,
name: 'Example Product',
price: 29.99,
description: 'A sample product',
imageUrl: 'https://example.com/image.jpg',
category: 'Electronics',
rating: 4.5,
countryOfOrigin: 'Canada'
});
}, 500);
});
}
type ProductType = Awaited<ReturnType<typeof fetchProduct>>; // type ProductType = Product
function displayProductDetails(product: ProductType) {
console.log(`Product Name: ${product.name}`);
console.log(`Price: ${product.price} ${product.countryOfOrigin === 'Canada' ? 'CAD' : (product.countryOfOrigin === 'USA' ? 'USD' : 'EUR')}`);
}
fetchProduct(123).then(displayProductDetails);
ãã®äŸã§ã¯ãProduct
ã€ã³ã¿ãŒãã§ãŒã¹ãšãAPIãã補åã®è©³çްãååŸããfetchProduct
颿°ãå®çŸ©ããŸããAwaited
ãšReturnType
ã䜿çšããŠãfetchProduct
颿°ã®æ»ãå€ã®åããProduct
åãæœåºããdisplayProductDetails
颿°ãåãã§ãã¯ã§ããããã«ããŸãã
2. åœéåïŒi18nïŒ
ãã±ãŒã«ã«åºã¥ããŠç°ãªãæååãè¿ãç¿»èš³é¢æ°ããããšããŸããinfer
ã䜿çšããŠããã®é¢æ°ã®æ»ãå€ã®åãåå®å
šã«æœåºã§ããŸãã
interface Translations {
greeting: string;
farewell: string;
welcomeMessage: (name: string) => string;
}
const enTranslations: Translations = {
greeting: 'Hello',
farewell: 'Goodbye',
welcomeMessage: (name: string) => `Welcome, ${name}!`,
};
const frTranslations: Translations = {
greeting: 'Bonjour',
farewell: 'Au revoir',
welcomeMessage: (name: string) => `Bienvenue, ${name}!`,
};
function getTranslation(locale: 'en' | 'fr'): Translations {
return locale === 'en' ? enTranslations : frTranslations;
}
type TranslationType = ReturnType<typeof getTranslation>;
function greetUser(locale: 'en' | 'fr', name: string) {
const translations = getTranslation(locale);
console.log(translations.welcomeMessage(name));
}
greetUser('fr', 'Jean'); // Output: Bienvenue, Jean!
ããã§ãTranslationType
ã¯Translations
ã€ã³ã¿ãŒãã§ãŒã¹ãšããŠæšæž¬ãããgreetUser
颿°ã翻蚳ãããæååã«ã¢ã¯ã»ã¹ããããã®æ£ããåæ
å ±ãæã€ããšãä¿èšŒãããŸãã
3. APIã¬ã¹ãã³ã¹ã®åŠç
APIãæäœããå Žåãã¬ã¹ãã³ã¹æ§é ã¯è€éã«ãªãå¯èœæ§ããããŸããinfer
ã¯ããã¹ããããAPIã¬ã¹ãã³ã¹ããç¹å®ã®ããŒã¿åãæœåºããã®ã«åœ¹ç«ã¡ãŸãã
interface ApiResponse<T> {
status: number;
data: T;
message?: string;
}
interface UserData {
id: number;
username: string;
email: string;
profile: {
firstName: string;
lastName: string;
country: string;
language: string;
}
}
async function fetchUser(userId: number): Promise<ApiResponse<UserData>> {
// Simulate API call
return new Promise((resolve) => {
setTimeout(() => {
resolve({
status: 200,
data: {
id: userId,
username: 'johndoe',
email: 'john.doe@example.com',
profile: {
firstName: 'John',
lastName: 'Doe',
country: 'USA',
language: 'en'
}
}
});
}, 500);
});
}
type UserApiResponse = Awaited<ReturnType<typeof fetchUser>>;
type UserProfileType = UserApiResponse['data']['profile'];
function displayUserProfile(profile: UserProfileType) {
console.log(`Name: ${profile.firstName} ${profile.lastName}`);
console.log(`Country: ${profile.country}`);
}
fetchUser(123).then((response) => {
if (response.status === 200) {
displayUserProfile(response.data.profile);
}
});
ãã®äŸã§ã¯ãApiResponse
ã€ã³ã¿ãŒãã§ãŒã¹ãšUserData
ã€ã³ã¿ãŒãã§ãŒã¹ãå®çŸ©ããŸããinfer
ãšåã€ã³ããã¯ã¹ã䜿çšããŠãAPIã¬ã¹ãã³ã¹ããUserProfileType
ãæœåºããdisplayUserProfile
颿°ãæ£ããåãåãåãããã«ããŸãã
infer
ã䜿çšããããã®ãã¹ããã©ã¯ãã£ã¹
- ã·ã³ãã«ã«ä¿ã€ïŒå¿
èŠãªå Žåã«ã®ã¿
infer
ã䜿çšããŸãã䜿ãããããšãã³ãŒãã®å¯èªæ§ãšç解床ãäœäžããå¯èœæ§ããããŸãã - åãããã¥ã¡ã³ãåããïŒæ¡ä»¶ä»ãåãš
infer
ã¹ããŒãã¡ã³ããäœãããŠãããã説æããã³ã¡ã³ãã远å ããŸãã - åããã¹ãããïŒTypeScriptã®åãã§ãã¯ã䜿çšããŠãåãæåŸ ã©ããã«åäœããŠããããšã確èªããŸãã
- ããã©ãŒãã³ã¹ãèæ ®ããïŒè€éãªæ¡ä»¶ä»ãåã¯ãã³ã³ãã€ã«æéã«åœ±é¿ãäžããå ŽåããããŸããåã®è€éãã«æ³šæããŠãã ããã
- ãŠãŒãã£ãªãã£åã䜿çšããïŒTypeScriptã¯ãã«ã¹ã¿ã
infer
ã¹ããŒãã¡ã³ããå¿ èŠãšããã«ãå€ãã®äžè¬çãªåæäœã¿ã¹ã¯ãç°¡çŽ åã§ãããããã€ãã®çµã¿èŸŒã¿ãŠãŒãã£ãªãã£åïŒäŸïŒReturnType
ãAwaited
ïŒãæäŸããŸãã
äžè¬çãªèœãšã穎
- 誀ã£ãæšè«ïŒå Žåã«ãã£ãŠã¯ãTypeScriptãæåŸ ã©ããã§ã¯ãªãåãæšæž¬ããããšããããŸããåå®çŸ©ãšæ¡ä»¶ãå確èªããŠãã ããã
- 埪ç°äŸåé¢ä¿ïŒ
infer
ã䜿çšããŠååž°åãå®çŸ©ããå Žåã¯æ³šæãå¿ èŠã§ãã埪ç°äŸåé¢ä¿ãã³ã³ãã€ã«ãšã©ãŒãçºçããå¯èœæ§ããããŸãã - é床ã«è€éãªåïŒçè§£ãšä¿å®ãé£ãããé床ã«è€éãªæ¡ä»¶ä»ãåãäœæããããšã¯é¿ããŠãã ãããããããããå°ããããã管çããããåã«åå²ããŸãã
infer
ã®ä»£æ¿ææ®µ
infer
ã¯åŒ·åãªããŒã«ã§ããã代æ¿ã¢ãããŒããããé©åãªç¶æ³ããããŸãã
- åã¢ãµãŒã·ã§ã³ïŒå Žåã«ãã£ãŠã¯ãåã¢ãµãŒã·ã§ã³ã䜿çšããŠãæšæž¬ãã代ããã«å€ã®åãæç€ºçã«æå®ã§ããŸãããã ããåã¢ãµãŒã·ã§ã³ã¯åãã§ãã¯ããã€ãã¹ããå¯èœæ§ããããããæ³šæããŠãã ããã
- åã¬ãŒãïŒåã¬ãŒãã¯ãå®è¡æãã§ãã¯ã«åºã¥ããŠå€ã®åãçµã蟌ãããã«äœ¿çšã§ããŸããããã¯ãå®è¡æã®æ¡ä»¶ã«åºã¥ããŠç°ãªãåãåŠçããå¿ èŠãããå Žåã«åœ¹ç«ã¡ãŸãã
- ãŠãŒãã£ãªãã£åïŒTypeScriptã¯ãã«ã¹ã¿ã
infer
ã¹ããŒãã¡ã³ããå¿ èŠãšããã«ãå€ãã®äžè¬çãªåæäœã¿ã¹ã¯ãåŠçã§ããè±å¯ãªãŠãŒãã£ãªãã£åãæäŸããŠããŸãã
çµè«
TypeScriptã®infer
ããŒã¯ãŒãã¯ãæ¡ä»¶ä»ãåãšçµã¿åããããšãé«åºŠãªåæäœæ©èœãè§£ãæŸã¡ãŸããããã«ãããè€éãªåæ§é ããç¹å®ã®åãæœåºããããå
ç¢ã§ä¿å®æ§ãé«ããåå®å
šãªã³ãŒããèšè¿°ã§ããŸãã颿°ã®æ»ãå€ã®åãæšæž¬ããããšããããªããžã§ã¯ãåããããããã£ãæœåºããããšãŸã§ãå¯èœæ§ã¯åºç¯å²ã«åã³ãŸãããã®ã¬ã€ãã§æŠèª¬ãããŠããååãšãã¹ããã©ã¯ãã£ã¹ãçè§£ããããšã§ãinfer
ãæå€§éã«æŽ»çšããTypeScriptã®ã¹ãã«ãåäžãããããšãã§ããŸããåãããã¥ã¡ã³ãåãã培åºçã«ãã¹ãããå¿
èŠã«å¿ããŠä»£æ¿ã¢ãããŒããæ€èšããããšãå¿ããªãã§ãã ãããinfer
ããã¹ã¿ãŒããããšã§ãæ¬åœã«è¡šçŸåè±ãã§åŒ·åãªTypeScriptã³ãŒããèšè¿°ã§ããããã«ãªããæçµçã«åªãããœãããŠã§ã¢ã«ã€ãªãããŸãã